package com.duolebo.uteped_sdk.utils;

import android.annotation.SuppressLint;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;

import com.duolebo.uteped_sdk.UTEPedMethodCallHandler;
import com.yc.nadalsdk.ble.open.UteBleClient;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 一键连接流程
 * 1、连上ble 4.0
 * 2、查询bt3.0状态
 * 3、搜索BT3.0
 * 4、发起配对bt3.0 createBond
 * 5、连接Headset  connectBondDeviceHeadset
 * 6、连接A2dp  connectBondDeviceA2dp
 *
 * 一键解绑流程：
 * 1、先断开耳机（Headset）disconnectBondDeviceHeadset；
 * 2、再断开音频（A2dp）disconnectBondDeviceA2dp；
 * 3、收到A2dp断连广播后，发送关闭BT3.0
 * 4、收到回调后断开BLE4.0
 * 5、removeband
 */

public class BluetoothHelper {

    private final static String TAG = "BluetoothHelper";
    private final static int STEP_IDLE = 0;
    private final static int STEP_DISCOVERY = 1;
    private final static int STEP_CREATE_BOND = 20;
    private final static int STEP_BONDING = 21;
    private final static int STEP_REMOVE_BOND = 22;
    private final static int STEP_HEADSET_CONNECTING = 30;
    private final static int STEP_HEADSET_CONNECTED = 31;
    private final static int STEP_HEADSET_DISCONNECTED = 32;
    private final static int STEP_A2DP_CONNECTING = 40;
    private final static int STEP_A2DP_CONNECTED = 41;
    private final static int STEP_A2DP_DISCONNECTED = 42;

    private BluetoothAdapter mBluetoothAdapter = null;
    private BluetoothHeadset mBluetoothHeadset = null;
    private BluetoothA2dp mBluetoothA2dp = null;
    private BluetoothDevice mCurrentDevice = null;
    private Context mContext;
    private int step = STEP_IDLE;
    private boolean mConnectHeadSet = false;
    private boolean mConnectA2dp = false;
    private int bondValue = 1;
    private int tryBondCount = 2;
    private boolean bindBT3Start = false;
    private boolean bindNotFound = false;
    private boolean unbindBT3Start = false;

    @SuppressLint("StaticFieldLeak")
    private static BluetoothHelper instance;
    public static BluetoothHelper getInstance() {
        if (instance == null) {
            instance = new BluetoothHelper();
        }

        return instance;
    }


    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            try {
                String action = intent.getAction();
                Logger.d(TAG, "receive action: " + action);
                // When discovery finds a device
                if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                    // Get the BluetoothDevice object from the Intent
                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    if (isCurrentDevice(device)) {
                        Logger.d(TAG, "find target device, stop discovery");
                        bindBT3Start = false;
                        mBluetoothAdapter.cancelDiscovery();
                        tryPairBT3();
                    }
                }
                else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
                    if (bindBT3Start) {
                        mBluetoothAdapter.startDiscovery();
                        bindBT3Start = false;
                        bindNotFound = true;
                    }
                    else if(bindNotFound) {
                        tryPairBT3();
                        bindNotFound = false;
                    }
                }
                else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    int state = Objects.requireNonNull(device).getBondState();
                    Logger.d(TAG, "device name:" + device.getName() + " state:" + state);
                    if (isCurrentDevice(device)) {
                        switch (state) {
                            case BluetoothDevice.BOND_BONDED:
                                if (mConnectHeadSet)
                                    connectBondDeviceHeadset(mCurrentDevice, mBluetoothAdapter);
                                else if (mConnectA2dp)
                                    connectBondDeviceA2dp(mCurrentDevice, mBluetoothAdapter);
                                break;

                            case BluetoothDevice.BOND_BONDING:
                                step = STEP_BONDING;
                                break;

                            case BluetoothDevice.BOND_NONE:
                                if (mConnectHeadSet && step == STEP_BONDING && UteBleClient.getUteBleClient().isConnected()) {
                                    if (tryBondCount > 0) {
                                        tryPairBT3();
                                        tryBondCount --;
                                    }
                                    else {
                                        bondValue = 1;
                                    }
                                }
                                break;
                        }

                        if (bluetoothOptionListener != null) {
                            bluetoothOptionListener.onBondState(state);
                        }
                    }
                }
                else if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, -1);
                    if (state == BluetoothHeadset.STATE_CONNECTED && isCurrentDevice(device) && mConnectA2dp) {
                        Logger.d(TAG, "find target headset device");
                    }
                    else if (device != null) {
                        Logger.d(TAG, "headset device :" + device.getName() + " " + device.getAddress());
                        if (isCurrentDevice(device) && unbindBT3Start) {
                            removeBond();
                        }
                    }
                }
                else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    int state = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, -1);
                    if (state == BluetoothA2dp.STATE_CONNECTED && isCurrentDevice(device)) {
                        Logger.d(TAG, "device a2dp connect success");
                    }
                    else if (device != null) {
                        Logger.d(TAG, "headset device :" + device.getName() + " " + device.getAddress());
                        if (!mConnectHeadSet && isCurrentDevice(device) && unbindBT3Start) {
                            removeBond();
                        }
                    }
                }
                else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
                    int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
                    switch (state) {
                        case BluetoothAdapter.STATE_OFF:
                            // 蓝牙已关闭
                            UTEPedMethodCallHandler.shareInstance().publishLocal(Const.ACTION_BLUETOOTH_STATE_OFF, "");
                            break;
                        case BluetoothAdapter.STATE_TURNING_OFF:
                            // 蓝牙正在关闭
                            break;
                        case BluetoothAdapter.STATE_ON:
                            // 蓝牙已打开
                            DeviceHelper.getInstance().autoConnectDevice();
                            UTEPedMethodCallHandler.shareInstance().publishLocal(Const.ACTION_BLUETOOTH_STATE_ON, "");
                            break;
                        case BluetoothAdapter.STATE_TURNING_ON:
                            // 蓝牙正在打开
                            break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };

    private final BluetoothProfile.ServiceListener mBluetoothListener = new BluetoothProfile.ServiceListener() {
        @Override
        public void onServiceConnected(int profile, BluetoothProfile bluetoothProfile) {
            if (profile == BluetoothProfile.HEADSET) {
                mBluetoothHeadset = (BluetoothHeadset) bluetoothProfile;
            }
            else if (profile == BluetoothProfile.A2DP) {
                mBluetoothA2dp =  (BluetoothA2dp) bluetoothProfile;
            }
        }

        @Override
        public void onServiceDisconnected(int profile) {
            if (profile == BluetoothProfile.HEADSET) {
                mBluetoothHeadset = null;
            }
            else {
                mBluetoothA2dp = null;
            }
        }
    };

    public void init(Context context) {
        mContext = context;
        BluetoothManager mBluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
        if (mBluetoothManager != null) {
            mBluetoothAdapter = mBluetoothManager.getAdapter();
        }

        if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
            mBluetoothAdapter.getProfileProxy(context, mBluetoothListener, BluetoothProfile.HEADSET);
        }

        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        context.registerReceiver(mReceiver, filter);

        filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
        context.registerReceiver(mReceiver, filter);

        filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        context.registerReceiver(mReceiver, filter);

        filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_STARTED );
        context.registerReceiver(mReceiver, filter);

        filter = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
        context.registerReceiver(mReceiver, filter);

        filter = new IntentFilter(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
        context.registerReceiver(mReceiver, filter);

        filter = new IntentFilter(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
        context.registerReceiver(mReceiver, filter);
    }

    private void tryPairBT3() {
        Logger.d(TAG, "tryPairBT3...");
        if (!UteBleClient.getUteBleClient().isConnected()) {
            Logger.d(TAG, "device not connected.");
            return;
        }

        if (mCurrentDevice == null) {
            Logger.d(TAG, "device is empty.");
            return;
        }

        if (isBT3Bind()) {
            Logger.d(TAG, "BT3 is already bind.");
            return;
        }

        if (bondValue == 2) {
            mCurrentDevice.createBond();
        }
        else if (createBond(mCurrentDevice, bondValue)) {
            step = STEP_CREATE_BOND;
//            bondValue++;
        }
        else {
            Logger.d(TAG, "createBond 1 failed");
        }
    }

    public void bindBT3(BluetoothDevice device) {
        Logger.d(TAG, "bindBT3...." + device.getName());
        if (isBT3Bind()) {
            if (mConnectHeadSet)
                connectBondDeviceHeadset(mCurrentDevice, mBluetoothAdapter);
            else if (mConnectA2dp)
                connectBondDeviceA2dp(mCurrentDevice, mBluetoothAdapter);
        }
        else {
            tryPairBT3();
        }
    }

    public void bindBT3(BluetoothDevice device, boolean connectHeadSet, boolean connectA2dp) {
        Logger.d(TAG, "bindBT3....");
        this.step = STEP_DISCOVERY;
        this.mCurrentDevice = device;
        this.mConnectHeadSet = connectHeadSet;
        this.mConnectA2dp = connectA2dp;

//        tryPairBT3();
        if (isBT3Bind()) {
            if (mConnectHeadSet)
                connectBondDeviceHeadset(mCurrentDevice, mBluetoothAdapter);
            else if (mConnectA2dp)
                connectBondDeviceA2dp(mCurrentDevice, mBluetoothAdapter);
        }
        else {
            mBluetoothAdapter.startDiscovery();
            bindBT3Start = true;
        }
    }

    private boolean isCurrentDevice(BluetoothDevice device) {
        if (mCurrentDevice == null || device == null || device.getAddress() == null) {
            return false;
        }

        return device.getAddress().equals(mCurrentDevice.getAddress());
    }

    public boolean createBond(BluetoothDevice device, int a){
        Logger.d(TAG, "createBond " + a);
        try {
            Class btClass = device.getClass();

            Method createBondMethod = btClass.getMethod("createBond", int.class);
            Boolean returnValue = (Boolean) createBondMethod.invoke(device, a);
            return returnValue.booleanValue();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }

    /**
     * 解除配对
     * @param device device
     * @return result
     */
    private boolean removeBond(BluetoothDevice device){
        try {
            Class<?> btClass = device.getClass();
            Method removeBondMethod = btClass.getMethod("removeBond");
            Boolean returnValue = (Boolean) removeBondMethod.invoke(device);
            return returnValue.booleanValue();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }

    /**
     * 获取BluetoothHeadset对象并连接DeviceHeadset
     * @param device device
     * @param bluetoothAdapter bluetoothAdapter
     */
    public void connectBondDeviceHeadset(BluetoothDevice device, BluetoothAdapter bluetoothAdapter) {
        step = STEP_HEADSET_CONNECTING;
        bluetoothAdapter.getProfileProxy(mContext, new BluetoothProfile.ServiceListener() {
            @Override
            public void onServiceConnected(int profile, BluetoothProfile proxy) {
                BluetoothHeadset bluetoothHeadset = (BluetoothHeadset) proxy;
                Logger.d(TAG, "connectBondDeviceHeadset name:" + device.getName() +  "name:" + device.getAddress());
                int state = bluetoothHeadset.getConnectionState(device);
                if (isCurrentDevice(device) && state != BluetoothHeadset.STATE_CONNECTED && state != BluetoothHeadset.STATE_CONNECTING) {
                    boolean isConnect2 = connectBondDeviceHeadset(device, bluetoothHeadset);
                    Logger.d(TAG, "connectBondDeviceHeadset HEADSET=" + isConnect2);
                    if (isConnect2) {
                        step = STEP_HEADSET_CONNECTED;
                        if (mConnectA2dp) {
                            connectBondDeviceA2dp(device, bluetoothAdapter);
                        }
                    }
                    else {
                        step = STEP_HEADSET_DISCONNECTED;
                    }
                }
            }

            @Override
            public void onServiceDisconnected(int profile) {
                Logger.d(TAG, "connectBondDeviceHeadset...onServiceDisconnected " + profile);
            }
        }, BluetoothProfile.HEADSET);
    }

    /**
     * 连接DeviceHeadset
     * @param device device
     * @param bluetoothHeadset bluetoothHeadset
     * @return result
     */
    private boolean connectBondDeviceHeadset(BluetoothDevice device, BluetoothHeadset bluetoothHeadset) {
        setPriority(bluetoothHeadset, device, 100); //设置priority
        try {
            bluetoothHeadset.getClass().getMethod("connect", BluetoothDevice.class).invoke(bluetoothHeadset, device);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取BluetoothHeadset对象并断开连接DeviceHeadset
     * @param device device
     * @param bluetoothAdapter bluetoothAdapter
     */
    public void disconnectBondDeviceHeadset(BluetoothDevice device, BluetoothAdapter bluetoothAdapter) {
        bluetoothAdapter.getProfileProxy(mContext, new BluetoothProfile.ServiceListener() {
            @Override
            public void onServiceConnected(int profile, BluetoothProfile proxy) {
                BluetoothHeadset bluetoothHeadset = (BluetoothHeadset) proxy;
                int state = bluetoothHeadset.getConnectionState(device);//android 12 need BLUETOOTH_CONNECT permision
                Logger.d(TAG, "连接状态 Headset =" + state);
                if (state == BluetoothHeadset.STATE_CONNECTED) {
                    disconnectBondDeviceHeadset(device, bluetoothHeadset);
                    cancelUnbindTimer();
                    if (bluetoothOptionListener != null) {
                        bluetoothOptionListener.onUnbind(true);
                    }
                }
                else if (unbindBT3Start) {
                    removeBond();
                }
            }

            @Override
            public void onServiceDisconnected(int profile) {
                Logger.d(TAG, "disconnectBondDeviceHeadset...onServiceDisconnected " + profile);
            }
        }, BluetoothProfile.HEADSET);
    }

    /**
     * 断开连接BluetoothHeadset
     * @param device device
     * @param bluetoothHeadset bluetoothHeadset
     */
    private void disconnectBondDeviceHeadset(BluetoothDevice device, BluetoothHeadset bluetoothHeadset) {
        try {
            bluetoothHeadset.getClass().getMethod("disconnect", BluetoothDevice.class).invoke(bluetoothHeadset, device);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 获取BluetoothA2dp对象并连接BluetoothA2dp
     * @param device device
     * @param bluetoothAdapter bluetoothAdapter
     */
    private void connectBondDeviceA2dp(BluetoothDevice device, BluetoothAdapter bluetoothAdapter) {
        step = STEP_A2DP_CONNECTING;
        bluetoothAdapter.getProfileProxy(mContext, new BluetoothProfile.ServiceListener() {
            @Override
            public void onServiceConnected(int profile, BluetoothProfile proxy) {
                BluetoothA2dp mA2dpService = (BluetoothA2dp) proxy;
                Logger.d(TAG, "connectBondDeviceA2dp name:" + device.getName() +  "name:" + device.getAddress());
                int state = mA2dpService.getConnectionState(device);
                if (isCurrentDevice(device) && state != BluetoothA2dp.STATE_CONNECTED && state != BluetoothA2dp.STATE_CONNECTING) {
                    boolean isConnect = connectBondDeviceA2dp(device, mA2dpService);
                    Logger.d(TAG, "connectBondDeviceA2dp A2DP=" + isConnect);
                    if (isConnect) {
                        step = STEP_A2DP_CONNECTED;
                    }
                    else {
                        step = STEP_A2DP_DISCONNECTED;
                    }
                }
            }

            @Override
            public void onServiceDisconnected(int profile) {
                Logger.d(TAG, "connectBondDeviceA2dp...onServiceDisconnected " + profile);
            }
        }, BluetoothProfile.A2DP);
    }

    /**
     * 连接BluetoothA2dp
     * @param device device
     * @param bluetoothA2dp bluetoothA2dp
     * @return result
     */
    private boolean connectBondDeviceA2dp(BluetoothDevice device, BluetoothA2dp bluetoothA2dp) {
        setPriority(bluetoothA2dp, device, 100); //设置priority
        try {
            bluetoothA2dp.getClass().getMethod("connect", BluetoothDevice.class)
                    .invoke(bluetoothA2dp, device);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取BluetoothA2dp对象并断开连接BluetoothA2dp
     * @param device device
     * @param bluetoothAdapter bluetoothAdapter
     */
    private void disconnectBondDeviceA2dp(BluetoothDevice device, BluetoothAdapter bluetoothAdapter) {
        bluetoothAdapter.getProfileProxy(mContext, new BluetoothProfile.ServiceListener() {
            @Override
            public void onServiceConnected(int profile, BluetoothProfile proxy) {
                BluetoothA2dp bluetoothA2dp = (BluetoothA2dp) proxy;
                int state = bluetoothA2dp.getConnectionState(device);//android 12 need BLUETOOTH_CONNECT permision
                Logger.d(TAG, "连接状态 Headset =" + state);
                if (state == BluetoothA2dp.STATE_CONNECTED) {
                    disconnectBondDeviceA2dp(device, bluetoothA2dp);
                    step = STEP_HEADSET_CONNECTED;
                }
                else {
                    disconnectBondDeviceHeadset(device, mBluetoothAdapter);
                }
            }

            @Override
            public void onServiceDisconnected(int profile) {
                Logger.d(TAG, "disconnectBondDeviceA2dp...onServiceDisconnected " + profile);
            }
        }, BluetoothProfile.A2DP);
    }

    /**
     * 断开连接BluetoothA2dp
     * @param device device
     * @param bluetoothA2dp bluetoothA2dp
     */
    private void disconnectBondDeviceA2dp(BluetoothDevice device, BluetoothA2dp bluetoothA2dp) {
        setPriority(bluetoothA2dp, device, 100); //设置priority
        try {
            bluetoothA2dp.getClass().getMethod("disconnect", BluetoothDevice.class).invoke(bluetoothA2dp, device);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void setPriority(BluetoothA2dp mA2dpService, BluetoothDevice device, int priority) {
        if (mA2dpService == null) return;
        try {//通过反射获取BluetoothA2dp中setPriority方法（hide的），设置优先级
            Method connectMethod = BluetoothA2dp.class.getMethod("setPriority",
                    BluetoothDevice.class, int.class);
            connectMethod.setAccessible(true);
            connectMethod.invoke(mA2dpService, device, priority);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void setPriority(BluetoothHeadset mHeadsetService, BluetoothDevice device, int priority) {
        if (mHeadsetService == null) return;
        try {//通过反射获取BluetoothA2dp中setPriority方法（hide的），设置优先级
            Method connectMethod = BluetoothHeadset.class.getMethod("setPriority",
                    BluetoothDevice.class, int.class);
            connectMethod.setAccessible(true);
            connectMethod.invoke(mHeadsetService, device, priority);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public boolean isBT3Bind() {
        boolean res = false;
//        return DeviceHelper.getInstance().read(BT3_BIND, false);
        Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();
        for (BluetoothDevice device:bondedDevices) {
            if (isCurrentDevice(device)) {
                return true;
            }
        }

        return res;
    }

    public void unbindBT3() {
        bondValue = 1;
        unbindBT3Start = true;
        setUnbindTimeout();
        mBluetoothAdapter.cancelDiscovery();
        if (isBT3Bind()) {
            try {
                if (this.mConnectA2dp) {
                    disconnectBondDeviceA2dp(mCurrentDevice, mBluetoothAdapter);
                    this.mConnectA2dp = false;
                }
                else if (this.mConnectHeadSet) {
                    disconnectBondDeviceHeadset(mCurrentDevice, mBluetoothAdapter);
                    this.mConnectHeadSet = false;
                }
                else {
                    removeBond();
                }

//                removeBond();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

//        mCurrentDevice = null;
    }

    public void removeBond() {
        step = STEP_REMOVE_BOND;
        removeBond(mCurrentDevice);
        step = STEP_IDLE;
        mCurrentDevice = null;
        unbindBT3Start = false;
    }

    Timer unbindTimer;
    private void setUnbindTimeout() {
        cancelUnbindTimer();
        unbindTimer = new Timer();
        unbindTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                if (bluetoothOptionListener != null) {
                    bluetoothOptionListener.onUnbind(false);
                }
                unbindBT3Start = false;
            }
        }, 5 * 1000L);
    }

    private void cancelUnbindTimer() {
        if (unbindTimer != null) {
            unbindTimer.cancel();
            unbindTimer = null;
        }
    }

    public void setBluetoothOptionListener(BluetoothOptionListener listener) {
        this.bluetoothOptionListener = listener;
    }

    public void turnOn() {
        if (mBluetoothAdapter != null && !mBluetoothAdapter.isEnabled()) {
            PermissionHelper.getInstance().checkBluetoothPermissions(Tools.getActivity(), success -> {
//                mBluetoothAdapter.enable();
                Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                enableBtIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                Tools.getApplicationContext().startActivity(enableBtIntent);
            });
        }
    }

    public void turnOff() {
        if (mBluetoothAdapter != null && !mBluetoothAdapter.isEnabled()) {
            mBluetoothAdapter.disable();
        }
    }

    private BluetoothOptionListener bluetoothOptionListener;
    public interface BluetoothOptionListener {
        void onUnbind(boolean success);
        void onBondState(int state);
    }

}
